<?php
declare(strict_types=1);

namespace lib\data;


class Category extends \lib\sync\SyncableData {
    use traits\MidTrait;

    protected static string $SyncKey = 'Category';
    protected static string $TableName = 'el_category';

    public int $id;
    public int $mid;
    public string $name;
    public int $num;
    public int $sortid;
    public bool $def;
    public int $createtime;

    protected static array $propNamesC = ['id','mid', 'sortid', 'name', 'num', 'def', 'createtime'];
    protected static array $propNamesU = ['id','mid', 'sortid', 'name', 'num', 'def', 'createtime'];
    protected static array $propNamesJ = ['id','mid', 'sortid', 'name', 'num', 'def', 'createtime'];

    protected static array $def_category = [];
    
    public function __construct(array &$data)
    {
        parent::__construct($data);
        $this->traitInit();
    }

    public static function getDefaultCategory(int $mid) :?static {
        return static::$def_category[$mid] ?? null;
    }

    public static function Move(int $mid , int $fromId, int $toId) :array {
        $from = \lib\data\Category::get($fromId);
        $to = \lib\data\Category::get($toId);

        if(null === $from || null === $to){
            return ['success'=>false, 'message'=>'分类不存在.'];
        }

        if($from->mid !== $mid || $to->mid !== $mid){
            return ['success'=>false, 'message'=>'分类不存在.'];
        }
        
        if($from->id === $to->id){
            return ['success'=>false, 'message'=>'不能移动到自己.'];
        }

        $toSortId = $to->sortid;
        $moveFront = $from->sortid > $toSortId;
        $syncObject = new \lib\sync\Synchronize($mid);
        $db = new \sys\Db();
        $db->startTrans();
        try{
            $db->table('el_category')->where([['id', '=', $from->id]])->lock(true)->field('id')->find();
            if($moveFront){
                # 前移
                $moves= array_column($db->table('el_category')->where([['mid', '=', $mid], ['sortid', 'BETWEEN', [$toSortId, $from->sortid-1]]])->lock(true)->field('id')->select() ?? [], 'id');
                $sqls = [
                    $db->table('el_category')->where([['mid', '=', $mid], ['sortid', 'BETWEEN', [$toSortId, $from->sortid-1]]])->inc('sortid', 1)->subUpdate(),
                    $db->table('el_category')->where([['id', '=', $from->id]])->subUpdate(['sortid'=>$toSortId]),
                ];
            }else{
                # 后移
                $moves= array_column($db->table('el_category')->where([['mid', '=', $mid], ['sortid', 'BETWEEN', [$from->sortid+1, $toSortId]]])->lock(true)->field('id')->select() ?? [], 'id');
                $sqls = [
                    $db->table('el_category')->where([['mid', '=', $mid], ['sortid', 'BETWEEN', [$from->sortid+1, $toSortId]]])->dec('sortid', 1)->subUpdate(),
                    $db->table('el_category')->where([['id', '=', $from->id]])->subUpdate(['sortid'=>$toSortId]),
                ];
            }
            $db->batch_query($sqls, \sys\Db::SQL_UPDATE);
            $db->commit();

            if($moveFront){
                # 前移
                foreach($moves as $id){
                    $gds = \lib\data\Category::get($id);
                    $gds->sortid += 1;
                    static::$mid_cache[$mid][$gds->sortid] = $gds;
                    $syncObject->up('Category', $gds->id, ['sortid'=>$gds->sortid]);
                }
            }else{
                # 后移
                foreach($moves as $id){
                    $gds = \lib\data\Category::get($id);
                    $gds->sortid -= 1;
                    static::$mid_cache[$mid][$gds->sortid] = $gds;
                    $syncObject->up('Category', $gds->id, ['sortid'=>$gds->sortid]);
                }
            }
            $from->sortid = $toSortId;
            static::$mid_cache[$mid][$toSortId] = $from;
            $syncObject->up('Category', $from->id, ['sortid'=>$from->sortid]);
            \lib\Merchant::get($mid)->push(['event'=>'sync','data'=>$syncObject->makeSyncData($db)]);

            return ['success'=>true, 'message'=>'调整成功.'];
        }catch(\Exception $e){
            $db->rollback();
            return ['success'=>false, 'message'=>$e->getMessage()];
        }
    }

    public function destroy(int $mid){
        if($this->mid !== $mid){
            return ['success'=>false, 'message'=>'分类不存在.','data'=>$this];
        }
        $db = new \sys\Db();

        $gids = []; $sqls = [];
        # 更新商品分类属性
        $syncObject = new \lib\sync\Synchronize($mid);
        $allGoods = \lib\data\Components::getByCate($this->id);
        foreach($allGoods as $goods){
            if(null !== ($changed = $goods->update([
                'category_ids'=>array_diff($goods->category_ids, [$this->id]),
            ]))){
                $syncObject->up('Goods', $goods->id, $changed);
                $gids[] = $goods->id;
                $sqls[] = $db->table('tp_nb_goods_rack')->where([['id','=', $goods->id]])->subUpdate($changed);
            }
        }
        $this->remove();
        $sqls[] = $db->table('el_category')->where([['id', '=', $this->id]])->subDelete();
        $db->startTrans();
        try{
            if(count($gids)> 0){
                $asql = [
                    $db->table('el_category')->where([['id','=', $this->id]])->lock(true)->field(['id'])->subFind(),
                    $db->table('tp_nb_goods_rack')->where([['id','IN', $this->id]])->lock(true)->field(['id'])->subSelect(),
                ];
            }else{
                $asql = [
                    $db->table('el_category')->where([['id','=', $this->id]])->lock(true)->field(['id'])->subFind(),
                ];
            }

            $db->batch_query([
                ...$asql,
                ...$sqls
            ], \sys\Db::SQL_UPDATE);

            $db->commit();
            $syncObject->rm('Category', $this->id);
            \lib\Merchant::get($this->mid)->push(['event'=>'sync','data'=>$syncObject->makeSyncData($db)]);
            return ['success'=>true, 'message'=>'删除成功.', 'gids'=>$gids];
        }catch(\Exception $e){
            $db->rollback();

            return ['success'=>false, 'message'=>$e->getMessage()];
        }
    }

    public static function save(int $mid, array $data) :array {
        if(isset($data['id'])){
            $cate = static::get((int)$data['id']);
            if(null === $cate || $cate->mid !== $mid){
                return ['success'=>false, 'message'=>'分类不存在.'];
            }

            if(null == ($changed = $cate->update(['name'=>$data['name']]))){
                return ['success'=>false, 'message'=>'分类没有修改.'];
            }
            $db = new \sys\Db();
            $db->table('el_category')->where([['id', '=', $cate->id]])->update($changed);
            $syncObject = new \lib\sync\Synchronize($mid, $db);
            $syncObject->up('Category', $cate->id, $changed);
            return ['success'=>true, 'message'=>'修改成功.', 'data'=>$cate];
        }else{
            $row = [
                'mid'=>$mid,
                'name'=>$data['name'],
                'num'=>0,
                'def'=>false,
                'createtime'=>time(),
            ];
            $db = new \sys\Db();
            $db->startTrans();
            try{
                $row['sortid'] = $db->table('el_category')->where([['mid','=', $mid]])->field(['COALESCE(MAX(`sortid`)+1,1) AS `sortid`'])->find()['sortid'];
                $row['id'] = $db->table('el_category')->insert($row);
                $db->commit();

                $cate = \lib\data\Category::createInstance($row);
                $syncObject = new \lib\sync\Synchronize($mid, $db);
                $syncObject->put('Category', $row);
                return ['success'=>true, 'message'=>'创建成功.', 'data'=>$cate];
            }catch(\PDOException $e){
                $db->rollback();
                return ['success'=>false, 'message'=>$e->getMessage()];
            }
        }
    }

    static protected function buildSyncSelect(\sys\Db $db, int $mid, int $time, int $lastId = 0) :\sys\SqlBuilder {
        if($lastId == 0){
            return $db->table(static::$TableName)->where([
                ['mid', '=', $mid],
            ])->order('id ASC');
        }
        return $db->table(static::$TableName)->where([
            ['mid', '=', $mid],
            ['id', '>', $lastId],
        ])->order('id ASC');
    }
}